package pers.ljf.escloud.service.modules.app.impl;

import com.aliyun.oss.OSS;
import com.aliyun.oss.model.*;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.google.gson.Gson;
import lombok.extern.slf4j.Slf4j;
import org.joda.time.LocalDateTime;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;
import pers.ljf.escloud.common.dto.FileItemDTO;
import pers.ljf.escloud.common.dto.OSSClientInfo;
import pers.ljf.escloud.common.dto.SystemFrontConfigDTO;
import pers.ljf.escloud.common.enums.FileTypeEnum;
import pers.ljf.escloud.common.req.FileListReq;
import pers.ljf.escloud.common.req.GetClientReq;
import pers.ljf.escloud.common.utils.DateUtils;
import pers.ljf.escloud.common.utils.StringUtils;
import pers.ljf.escloud.common.utils.UserInfoUtils;
import pers.ljf.escloud.dao.entity.app.Department;
import pers.ljf.escloud.service.modules.app.BucketService;
import pers.ljf.escloud.service.modules.app.DepartmentService;
import pers.ljf.escloud.service.modules.app.EnterpriseService;
import pers.ljf.escloud.service.modules.app.FileService;

import javax.annotation.Resource;
import java.io.UnsupportedEncodingException;
import java.net.URL;
import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;

@Service
@Slf4j
public class FileServiceImpl implements FileService {


    @Resource
    private DepartmentService departmentService;

    @Resource
    private EnterpriseService enterpriseService;


    private static final int EXPIRE_HOUR = 12;

    @Resource
    private BucketService bucketService;

    @Override
    public long deleteByPrefix(Long userId, String bucketName, String prefix, OSS ossClient, boolean updateSpace) {
        String nextMarker = null;
        ObjectListing objectListing = null;
        long deletedSizeB = 0l;
        do {
            ListObjectsRequest listObjectsRequest = new ListObjectsRequest(bucketName)
                    .withPrefix(prefix)
                    .withMarker(nextMarker);

            objectListing = ossClient.listObjects(listObjectsRequest);
            if (objectListing.getObjectSummaries().size() > 0) {
                List<String> keys = new ArrayList<String>();
                for (OSSObjectSummary s : objectListing.getObjectSummaries()) {
                    deletedSizeB += s.getSize();
                    keys.add(s.getKey());
                }
                DeleteObjectsRequest deleteObjectsRequest = new DeleteObjectsRequest(bucketName).withKeys(keys).withEncodingType("url");
                DeleteObjectsResult deleteObjectsResult = ossClient.deleteObjects(deleteObjectsRequest);
                List<String> deletedObjects = deleteObjectsResult.getDeletedObjects();
                try {
                    for (String obj : deletedObjects) {
                        String deleteObj = URLDecoder.decode(obj, "UTF-8");
                        log.info("deleteObj:{}", deleteObj);
                    }
                    int deletedSize = deletedObjects.size();
                    if (updateSpace && deletedSizeB > 1024 * 1024) {
                        departmentService.updateSpace(userId, -1 * deletedSizeB / 1024 / 1024);
                        enterpriseService.updateSpace(userId, -1 * deletedSizeB / 1024 / 1024);
                    }
                    return deletedSizeB / 1024 / 1024;
                } catch (UnsupportedEncodingException e) {
                    e.printStackTrace();
                }
            }

            nextMarker = objectListing.getNextMarker();
        } while (objectListing.isTruncated());
        return 0;
    }

    /**
     * 返回是否是文件夹
     *
     * @param fullPath 格式为"/xxx/yyy"
     * @return
     */
    @Override
    public boolean isDeptFolder(String fullPath) {
        if (fullPath.contains(".")) {
            return false;
        }
        String path = StringUtils.getPath(fullPath);
        String item = StringUtils.getLastWord(fullPath);
        QueryWrapper<Department> wr = new QueryWrapper<Department>();
        if (null != path) {
            wr.eq("full_path_name", path);
        }
        wr.eq("name", item);
        return departmentService.getOne(wr) != null;
    }

    @Override
    public int deleteFile(String bucketName, String fullPath, OSS client) {
        List<String> keys = new ArrayList<>();
        keys.add(fullPath);
        log.info(keys.toString());
        DeleteObjectsResult deleteObjectsResult = client.deleteObjects(new DeleteObjectsRequest(bucketName).withKeys(keys));
        Assert.isTrue(deleteObjectsResult.getDeletedObjects().size() == 1, "删除文件失败");
//        client.deleteObject(bucketName,fullPath);
        return 1;
    }

    /**
     * 获取 S3 指定目录下的对象列表
     *
     * @return 指定目录下的对象列表
     */
    @Override
    public List<FileItemDTO> fileList(FileListReq req) {
        String path = req.getPath();
        Boolean isBase = req.getIsBase();
        OSS client = req.getClient();
        path = StringUtils.removeFirstSeparator(path);
        String fullPath;
        String bucketName;
        if (!isBase) {
            String basePath = UserInfoUtils.get().getBasePath();
            fullPath = StringUtils.removeFirstSeparator(StringUtils.getFullPath(basePath, path));
            bucketName = UserInfoUtils.get().getBucketName();
        } else {
            fullPath = path;
            bucketName = req.getBucketName();
        }
        List<FileItemDTO> fileItemList = new ArrayList<>();
        ObjectListing objectListing = client.listObjects(new ListObjectsRequest(bucketName, fullPath, "", "/", 1000));

        for (OSSObjectSummary s : objectListing.getObjectSummaries()) {
            FileItemDTO fileItemDTO = new FileItemDTO();
            if (!isBase && s.getKey().equals(fullPath)) {
                continue;
            }
            fileItemDTO.setName(s.getKey().substring(fullPath.length()));
            fileItemDTO.setKey(s.getKey());
            fileItemDTO.setSize(s.getSize());
            fileItemDTO.setTime(s.getLastModified());
            fileItemDTO.setTimeOuter(DateUtils.format(s.getLastModified(), DateUtils.DATE_TIME_PATTERN));
            fileItemDTO.setType(FileTypeEnum.FILE);
            fileItemDTO.setPath(path);
            URL url = client.generatePresignedUrl(bucketName, s.getKey(), new LocalDateTime().plusHours(EXPIRE_HOUR).toDate());
            fileItemDTO.setUrl(url.toString());
            fileItemList.add(fileItemDTO);
        }

        for (String commonPrefix : objectListing.getCommonPrefixes()) {
            FileItemDTO fileItemDTO = new FileItemDTO();
            if (Objects.equals(commonPrefix, "/")) {
                continue;
            }
            fileItemDTO.setName(commonPrefix.substring(fullPath.length(), commonPrefix.length() - 1));
            fileItemDTO.setType(FileTypeEnum.FOLDER);
            fileItemDTO.setPath(path);
            fileItemList.add(fileItemDTO);
        }

        return fileItemList;
    }

    @Override
    public boolean isFolder(String bucketName, OSS client, String fullPath) {
        return true;
    }

    @Override
    public SystemFrontConfigDTO getConfig() {
        String config = "{\n" +
                "  \"siteName\": \"ESCLOUD\",\n" +
                "  \"searchEnable\": false,\n" +
                "  \"username\": \"zhao\",\n" +
                "  \"domain\": \"http://localhost:8080\",\n" +
                "  \"customJs\": \"$(\\\"#List\\\").append(\\\"<div class='footer'><a href='https://beian.miit.gov.cn/' target='_blank'>冀ICP备17004571号-4</a></div>\\\")\\n\",\n" +
                "  \"customCss\": \".footer,\\n.footer a {\\n    text-align: center;\\n    background-color: transparent;\\n    color: #555;\\n}\",\n" +
                "  \"tableSize\": \"mini\",\n" +
                "  \"showOperator\": true,\n" +
                "  \"showDocument\": true,\n" +
                "  \"announcement\": \"\",\n" +
                "  \"showAnnouncement\": true,\n" +
                "  \"layout\": \"full\",\n" +
                "  \"readme\": null,\n" +
                "  \"debugMode\": false,\n" +
                "  \"defaultSwitchToImgMode\": false,\n" +
                "  \"showLinkBtn\": true,\n" +
                "  \"showShortLink\": true,\n" +
                "  \"showPathLink\": true,\n" +
                "  \"directLinkPrefix\": \"directlink\"\n" +
                "}";
        return new Gson().fromJson(config, SystemFrontConfigDTO.class);
    }

    /**
     * 移动文件
     *
     * @param info
     * @param source 源文件（夹）全路径,结尾、开头不带/
     * @param target 目标文件（夹）全路径,结尾、开头不带/
     */
    @Override
    public long moveObject(OSSClientInfo info, String source, String target) {
        OSS client = info.getClient();
        List<FileItemDTO> files = fileList(FileListReq.builder().client(client).bucketName(info.getBucketName()).path(source + "/").isBase(true).build());
        for (FileItemDTO file : files) {
            if (file.getType().equals(FileTypeEnum.FOLDER)) {
                continue;
            }
            String key = file.getKey();
            String newKey = key.replaceFirst(source, target);
            client.copyObject(info.getBucketName(), key, info.getBucketName(), newKey);
        }
        return deleteByPrefix(null, info.getBucketName(), source, client, false);
    }

    @Override
    public void updateSizeFromOldToNew(Long fromId, Long toId, long sizeMB) {
        long p = fromId;
        do {
            Department fromDept = departmentService.getById(p);
            long fromNewSpace = fromDept.getSpaceUsed() - sizeMB;
            fromDept.setSpaceUsed(fromNewSpace < 0 ? 0 : fromNewSpace);
            departmentService.updateById(fromDept);
            p = fromDept.getParentId();
        } while (p != 0);
        p = toId;
        do {
            Department toDept = departmentService.getById(p);
            long toNewSpace = toDept.getSpaceUsed() + sizeMB;
            Assert.isTrue(toNewSpace <= toDept.getSpaceAmount(), "新部门空间不足");
            toDept.setSpaceUsed(toNewSpace);
            departmentService.updateById(toDept);
            p = toDept.getParentId();
        } while (p != 0);
    }

    @Override
    public FileItemDTO getFileItem(String path) {
        String entName = path.substring(0, path.indexOf("/"));
        OSSClientInfo client = bucketService.getClient(GetClientReq.builder().entName(entName).build());
        List<FileItemDTO> list;
        list = fileList(FileListReq.builder().client(client.getClient()).path(path).bucketName(client.getBucketName()).isBase(true).build());
        Assert.isTrue(list != null && list.size() == 1, "文件不存在");
        return list.get(0);
    }
}
